#include "app_type.h"
#include "compiler.h"
#include "interfaces.h"

#if IFS_USART_EN

#include "M45x_USART.h"
#include "core.h"

#include "M451Series.h"
#include "sys.h"

static void (*m45x_usart_ontx[USART_NUM])(void *);
static void (*m45x_usart_onrx[USART_NUM])(void *, uint16_t data);

static void *m45x_usart_callback_param[USART_NUM];
static const UART_T *m45x_usarts[USART_NUM] =
{
#if USART_NUM >= 1
	UART0,
#endif
#if USART_NUM >= 2
	UART1,
#endif
#if USART_NUM >= 3
	UART2,
#endif
#if USART_NUM >= 4
	UART3,
#endif
#if USART_NUM >= 5
	UART4,
#endif
#if USART_NUM >= 6
	UART5
#endif
};


vsf_err_t m45x_usart_init(uint8_t index)
{
	uint8_t usart_idx = index & 0x0F;

#if __VSF_DEBUG__
	if (usart_idx >= USART_NUM)
	{
		return VSFERR_NOT_SUPPORT;
	}
#endif

	CLK->CLKSEL1 &= ~CLK_CLKSEL1_UARTSEL_Msk;
	CLK->CLKSEL1 |= (0x00 << CLK_CLKSEL1_UARTSEL_Pos); // HXT

	switch (usart_idx)
	{
	case 0:
		CLK->APBCLK0 |= CLK_APBCLK0_UART0CKEN_Msk;
		SYS->GPD_MFPL &= ~(SYS_GPD_MFPL_PD1MFP_Msk);
		SYS->GPD_MFPL |= (SYS_GPD_MFPL_PD1MFP_UART0_TXD);
		SYS->GPD_MFPL &= ~(SYS_GPD_MFPL_PD0MFP_Msk);
		SYS->GPD_MFPL |= (SYS_GPD_MFPL_PD0MFP_UART0_RXD);
		break;
	case 1:
		CLK->APBCLK0 |= CLK_APBCLK0_UART1CKEN_Msk;
		SYS->GPA_MFPL &= ~(SYS_GPA_MFPL_PA0MFP_Msk);
		SYS->GPA_MFPL |= (SYS_GPA_MFPL_PA0MFP_UART1_TXD);
		SYS->GPA_MFPL &= ~(SYS_GPA_MFPL_PA1MFP_Msk);
		SYS->GPA_MFPL |= (SYS_GPA_MFPL_PA1MFP_UART1_RXD);
		break;
	case 2:
		//CLK->APBCLK0 |= CLK_APBCLK0_UART2CKEN_Msk;
		break;
	case 3:
		//CLK->APBCLK0 |= CLK_APBCLK0_UART3CKEN_Msk;
		break;
	default:
		return VSFERR_NOT_SUPPORT;
	}
	
	return VSFERR_NONE;
}

vsf_err_t m45x_usart_fini(uint8_t index)
{
	return VSFERR_NONE;
}

vsf_err_t m45x_usart_config(uint8_t index, uint32_t baudrate,
								uint8_t datalength, uint8_t mode)
{
	UART_T *usart;
	uint8_t usart_idx = index & 0x0F;
	struct m45x_info_t *info;
	uint32_t module_hz = 0;
	uint32_t reg_line = 0;

#if __VSF_DEBUG__
	if (usart_idx >= USART_NUM)
	{
		return VSFERR_NOT_SUPPORT;
	}
#endif

	usart = (UART_T *)m45x_usarts[usart_idx];

	switch (datalength)
	{
	case 5:
		reg_line |= UART_WORD_LEN_5;
		break;
	case 6:
		reg_line |= UART_WORD_LEN_6;
		break;
	case 7:
		reg_line |= UART_WORD_LEN_7;
		break;
	case 8:
		reg_line |= UART_WORD_LEN_8;
		break;
	default:
		return VSFERR_INVALID_PARAMETER;
	}

	// mode:
	// bit0 - bit1: parity
	// ------------------------------------- bit2 - bit3: mode [nothing]
	// bit4       : stopbits
	reg_line |= (mode << 3) & 0x18;	//parity
	reg_line |= (mode >> 3) & 0x04;	//stopbits

	usart->FUNCSEL = 0x00;
	usart->LINE = reg_line;
	usart->FIFO = UART_FIFO_RFITL_8BYTES;
	usart->TOUT = 60;

	if (m45x_interface_get_info(&info))
	{
		return VSFERR_FAIL;
	}

	module_hz = info->osc_freq_hz;

	if(baudrate != 0)
	{
		usart->BAUD = UART_BAUD_BAUDM0_Msk | UART_BAUD_BAUDM1_Msk |
						module_hz / baudrate - 2;
	}
	else
	{
		return VSFERR_INVALID_PARAMETER;
	}
	
	usart->INTEN = UART_INTEN_RDAIEN_Msk | UART_INTEN_RXTOIEN_Msk |
					UART_INTEN_TOCNTEN_Msk;
	switch (usart_idx)
	{
	case 0:
		NVIC_EnableIRQ(UART0_IRQn);
		break;
	case 1:
		NVIC_EnableIRQ(UART1_IRQn);
		break;
	case 2:
		NVIC_EnableIRQ(UART2_IRQn);
		break;
	case 3:
		NVIC_EnableIRQ(UART3_IRQn);
		break;
	default:
		return VSFERR_NOT_SUPPORT;
	}
	
	return VSFERR_NONE;
}

vsf_err_t m45x_usart_config_callback(uint8_t index, void *p,
						void (*ontx)(void *), void (*onrx)(void *, uint16_t))
{
	uint8_t usart_idx = index & 0x0F;

#if __VSF_DEBUG__
	if (usart_idx >= USART_NUM)
	{
		return VSFERR_NOT_SUPPORT;
	}
#endif
 
	m45x_usart_ontx[usart_idx] = ontx;
	m45x_usart_onrx[usart_idx] = onrx;
	m45x_usart_callback_param[usart_idx] = p;

	return VSFERR_NONE;
}

vsf_err_t m45x_usart_tx(uint8_t index, uint16_t data)
{
	UART_T *usart;
	uint8_t usart_idx = index & 0x0F;

#if __VSF_DEBUG__
	if (usart_idx >= USART_NUM)
	{
		return VSFERR_NOT_SUPPORT;
	}
#endif
	usart = (UART_T *)m45x_usarts[usart_idx];

	usart->DAT = (uint8_t)data;
	usart->INTEN |= UART_INTEN_THREIEN_Msk;

	return VSFERR_NONE;
}

vsf_err_t m45x_usart_tx_isready(uint8_t index)
{
	UART_T *usart;
	uint8_t usart_idx = index & 0x0F;

#if __VSF_DEBUG__
	if (usart_idx >= USART_NUM)
	{
		return VSFERR_NOT_SUPPORT;
	}
#endif
	usart = (UART_T *)m45x_usarts[usart_idx];

	return (UART_IS_TX_EMPTY(usart)) ? VSFERR_NONE : VSFERR_NOT_READY;
}

uint16_t m45x_usart_rx(uint8_t index)
{
	UART_T *usart;
	uint8_t usart_idx = index & 0x0F;

#if __VSF_DEBUG__
	if (usart_idx >= USART_NUM)
	{
		return 0;
	}
#endif
	usart = (UART_T *)m45x_usarts[usart_idx];

	return usart->DAT;
}

vsf_err_t m45x_usart_rx_isready(uint8_t index)
{
	UART_T *usart;
	uint8_t usart_idx = index & 0x0F;

#if __VSF_DEBUG__
	if (usart_idx >= USART_NUM)
	{
		return VSFERR_NOT_SUPPORT;
	}
#endif
	usart = (UART_T *)m45x_usarts[usart_idx];

	return (UART_IS_RX_READY(usart)) ? VSFERR_NOT_READY : VSFERR_NONE;
}

vsf_err_t m45x_usart_tx_bytes(uint8_t index, uint8_t *data, uint16_t size)
{
	UART_T *usart;
	uint8_t usart_idx = index & 0x0F;
	uint16_t i;

#if __VSF_DEBUG__
	if (usart_idx >= USART_NUM)
	{
		return VSFERR_NOT_SUPPORT;
	}
#endif
	usart = (UART_T *)m45x_usarts[usart_idx];

	for (i = 0; i < size; i++)
	{
		usart->DAT = data[i];
	}
	usart->INTEN |= UART_INTEN_THREIEN_Msk;

	return VSFERR_NONE;
}

uint16_t m45x_usart_tx_get_avail_length(uint8_t index)
{
	UART_T *usart;
	uint8_t usart_idx = index & 0x0F;
#if __VSF_DEBUG__
	if (usart_idx >= USART_NUM)
	{
		return VSFERR_NOT_SUPPORT;
	}
#endif
	usart = (UART_T *)m45x_usarts[usart_idx];

	if (usart->FIFOSTS & UART_FIFOSTS_TXFULL_Msk)
	{
		return 0;
	}
	else
	{
		return 16 - ((usart->FIFOSTS & UART_FIFOSTS_TXPTR_Msk) >>
						UART_FIFOSTS_TXPTR_Pos);
	}
}

uint16_t m45x_usart_rx_bytes(uint8_t index, uint8_t *data, uint16_t size)
{
	UART_T *usart;
	uint8_t usart_idx = index & 0x0F;
	uint16_t i;

#if __VSF_DEBUG__
	if (usart_idx >= USART_NUM)
	{
		return VSFERR_NOT_SUPPORT;
	}
#endif
	usart = (UART_T *)m45x_usarts[usart_idx];

	for (i = 0; i < size; i++)
	{
		if (usart->FIFOSTS & (UART_FIFOSTS_RXFULL_Msk | UART_FIFOSTS_RXPTR_Msk))
		{
			data[i] = usart->DAT;
		}
		else
		{
			break;
		}
	}
	return i;
}

uint16_t m45x_usart_rx_get_data_length(uint8_t index)
{
	UART_T *usart;
	uint8_t usart_idx = index & 0x0F;
#if __VSF_DEBUG__
	if (usart_idx >= USART_NUM)
	{
		return VSFERR_NOT_SUPPORT;
	}
#endif
	usart = (UART_T *)m45x_usarts[usart_idx];

	if (usart->FIFOSTS & UART_FIFOSTS_RXFULL_Msk)
	{
		return 16;
	}
	else
	{
		return (usart->FIFOSTS & UART_FIFOSTS_RXPTR_Msk) >> UART_FIFOSTS_RXPTR_Pos;
	}
}

ROOTFUNC void UART0_IRQHandler(void)
{
	if (UART0->INTSTS & UART_INTSTS_RDAIF_Msk)
	{
		m45x_usart_onrx[0](m45x_usart_callback_param[0], UART0->DAT);
	}
	else if (UART0->INTSTS & UART_INTSTS_RXTOINT_Msk)
	{
		m45x_usart_onrx[0](m45x_usart_callback_param[0], UART0->DAT);
	}
	if (UART0->FIFOSTS & UART_FIFOSTS_TXEMPTYF_Msk)
	{
		UART0->INTEN &= (~UART_INTEN_THREIEN_Msk);
		m45x_usart_ontx[0](m45x_usart_callback_param[0]);
	}
}

ROOTFUNC void UART1_IRQHandler(void)
{
	if (UART1->INTSTS & UART_INTSTS_RDAIF_Msk)
	{
		m45x_usart_onrx[1](m45x_usart_callback_param[1], UART1->DAT);
	}
	else if (UART1->INTSTS & UART_INTSTS_RXTOINT_Msk)
	{
		m45x_usart_onrx[1](m45x_usart_callback_param[1], UART1->DAT);
	}
	if (UART1->FIFOSTS & UART_FIFOSTS_TXEMPTYF_Msk)
	{
		UART1->INTEN &= (~UART_INTEN_THREIEN_Msk);
		m45x_usart_ontx[1](m45x_usart_callback_param[1]);
	}
}

#endif

